﻿using System;
using System.Collections.Generic;
using System.Linq;
using mvcForum.Core;
using mvcForum.Core.Abstractions.Interfaces;
using mvcForum.Core.Events;
using mvcForum.Core.Interfaces.Events;
using mvcForum.Core.Specifications;
using System.Net.Mail;
using SimpleLocalisation;
using mvcForum.Core.Interfaces.Data;
using mvcForum.Core.Interfaces.Services;

namespace mvcForum.Web.Events {

	public class ModerationEventListener :
											IEventListener<NewPostEvent>,
											IEventListener<NewTopicEvent>,
											IEventListener<PostFlagUpdatedEvent>,
											IEventListener<TopicFlagUpdatedEvent> {

		private readonly IRepository<Topic> topicRepo;
		private readonly IRepository<Post> postRepo;
		private readonly IConfiguration configuration;
		private readonly IMailService mail;
		private readonly IRepository<ForumAccess> faRepo;
		private readonly IRepository<GroupMember> gmRepo;
		private readonly ILogger logger;
		private readonly TextManager textManager;

		public ModerationEventListener(IRepository<Topic> topicRepo, IRepository<Post> postRepo, IConfiguration configuration, IMailService mail, IRepository<ForumAccess> faRepo, IRepository<GroupMember> gmRepo, ILogger logger, TextManager textManager) {
			this.topicRepo = topicRepo;
			this.postRepo = postRepo;
			this.configuration = configuration;
			this.mail = mail;
			this.gmRepo = gmRepo;
			this.faRepo = faRepo;
			this.logger = logger;
			this.textManager = textManager;
		}

		public void Handle(Object payload) {
			if (payload is TopicFlagUpdatedEvent) {
				this.Handle((TopicFlagUpdatedEvent)payload);
			}
			else if (payload is PostFlagUpdatedEvent) {
				this.Handle((PostFlagUpdatedEvent)payload);
			}
			else if (payload is NewPostEvent) {
				this.Handle((NewPostEvent)payload);
			}
			else if (payload is NewTopicEvent) {
				this.Handle((NewTopicEvent)payload);
			}
			else {
				throw new ApplicationException("Unknown payload!");
			}
		}

		public void Handle(TopicFlagUpdatedEvent payload) {
			if (this.configuration.InformOnQuarantine) {
				Topic topic = this.topicRepo.Read(payload.TopicId);

				if (payload.OriginalFlag != TopicFlag.Quarantined && topic.Flag == TopicFlag.Quarantined) {
					Dictionary<String, String> moderators = this.GetModerators(topic.Forum);

					String subject = this.textManager.Get("TopicSubject", ns: "mvcForum.Web.Events.ModerationEventListener");
					String url = this.configuration.SiteURL;
					if (!url.EndsWith("/")) {
						url += "/";
					}
					url += "forum/moderate/index/" + topic.ForumId;
					String body = this.textManager.Get("TopicBody", ns: "mvcForum.Web.Events.ModerationEventListener", values: new { Title = topic.Title, Url = url, Reason = topic.Posts.OrderBy(p => p.Posted).First().EditReason });

					this.Send(moderators, subject, body);
				}
			}
		}

		public void Handle(PostFlagUpdatedEvent payload) {
			if (this.configuration.InformOnQuarantine) {
				Post post = this.postRepo.Read(payload.PostId);

				if (payload.OriginalFlag != PostFlag.Quarantined && post.Flag == PostFlag.Quarantined) {
					Dictionary<String, String> moderators = this.GetModerators(post.Topic.Forum);

					String subject = this.textManager.Get("PostSubject", ns: "mvcForum.Web.Events.ModerationEventListener");
					String url = this.configuration.SiteURL;
					if (!url.EndsWith("/")) {
						url += "/";
					}
					url += "forum/moderate/index/" + post.Topic.ForumId;
					String body = this.textManager.Get("PostBody", ns: "mvcForum.Web.Events.ModerationEventListener", values: new { Subject = post.Subject, Title = post.Topic.Title, Url = url, Reason = post.EditReason });

					this.Send(moderators, subject, body);
				}
			}
		}

		public void Handle(NewTopicEvent payload) {
			if (this.configuration.InformOnQuarantine) {
				Topic topic = this.topicRepo.Read(payload.TopicId);
				if (topic.Flag == TopicFlag.Quarantined) {
					Dictionary<String, String> moderators = this.GetModerators(topic.Forum);

					String subject = this.textManager.Get("TopicSubject", ns: "mvcForum.Web.Events.ModerationEventListener");
					String url = this.configuration.SiteURL;
					if (!url.EndsWith("/")) {
						url += "/";
					}
					url += "forum/moderate/index/" + topic.ForumId;
					String body = this.textManager.Get("TopicBody", ns: "mvcForum.Web.Events.ModerationEventListener", values: new { Title = topic.Title, Url = url, Reason = topic.Posts.OrderBy(p => p.Posted).First().EditReason });

					this.Send(moderators, subject, body);
				}
			}
		}

		public void Handle(NewPostEvent payload) {
			if (this.configuration.InformOnQuarantine) {
				Post post = this.postRepo.Read(payload.PostId);
				if (post.Flag == PostFlag.Quarantined) {
					Dictionary<String, String> moderators = this.GetModerators(post.Topic.Forum);

					String subject = this.textManager.Get("PostSubject", ns: "mvcForum.Web.Events.ModerationEventListener");
					String url = this.configuration.SiteURL;
					if (!url.EndsWith("/")) {
						url += "/";
					}
					url += "forum/moderate/index/" + post.Topic.ForumId;
					String body = this.textManager.Get("PostBody", ns: "mvcForum.Web.Events.ModerationEventListener", values: new { Subject = post.Subject, Title = post.Topic.Title, Url = url, Reason = post.EditReason });

					this.Send(moderators, subject, body);
				}
			}
		}

		private Dictionary<String, String> GetModerators(Forum forum) {
			Dictionary<String, String> moderators = new Dictionary<String, String>();

			IEnumerable<ForumAccess> accesses = faRepo.ReadMany(new ForumAccessSpecifications.SpecificForum(forum));
			List<Group> groups = new List<Group>();
			foreach (ForumAccess fa in accesses) {
				if ((fa.AccessMask.AccessFlag & AccessFlag.Moderator) == AccessFlag.Moderator) {
					if (!groups.Contains(fa.Group)) {
						groups.Add(fa.Group);
					}
				}
			}

			IEnumerable<ForumUser> users = new List<ForumUser>();
			foreach (Group group in groups) {
				IEnumerable<GroupMember> members = gmRepo.ReadMany(new GroupMemberSpecifications.SpecificGroup(group));
				users = users.Concat(members.Select(m => m.ForumUser));
			}

			return users.ToDictionary(u => u.EmailAddress, u => u.AuthorName(this.configuration));
		}

		private void Send(Dictionary<String, String> moderators, String subject, String body) {
			foreach (KeyValuePair<String, String> moderator in moderators) {
				try {
					this.mail.Send(new MailAddress(this.configuration.RobotEmailAddress, this.configuration.RobotName), new MailAddress[] { new MailAddress(moderator.Key, moderator.Value) }.ToList(), subject, body);

					logger.Log(EventType.Debug, String.Format("The e-mail to user {0} ({1}) has been sent.", moderator.Value, moderator.Key));
				}
				catch (Exception ex) {
					logger.Log(EventType.Error, String.Format("Could not send e-mail to {0}, MessageAdded", moderator.Key), ex);
				}
			}
		}

		public Byte Priority {
			get {
				return (Byte)EventPriority.Lowest;
			}
		}

		public Boolean UniqueEvent {
			get {
				return false;
			}
		}
	}
}
